/* dpg - Data Patterns Generator */

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
#include <error.h>
#include <errno.h>

#define MAX_PATTERN 100*1024
#define DEFAULT_PATTERN "1234567890"


int main(int argc, char *argv[])
{
    size_t n, data_size, pattern_len, pindex;
    char *data_size_str, *unitp;
    char pattern[MAX_PATTERN];
    int opt;
    char *hex_stringp, hex_byte[3];
    size_t hex_string_len;
    FILE *in_file;

    strncpy(pattern, DEFAULT_PATTERN, MAX_PATTERN - 1);
    pattern[MAX_PATTERN - 1] = '\0';
    pattern_len = strlen(pattern);
    while ((opt = getopt(argc, argv, "a:b:f:n:o:r:s:x:h")) != -1) {
        switch (opt) {
            case 'a':
                pattern_len = atoi(optarg);
                if (pattern_len > MAX_PATTERN) {
                    fprintf(stderr, "Error: Pattern length too long. "
                            "Current limit is: %u.\n", MAX_PATTERN);
                    exit(EXIT_FAILURE);
                }
                pindex = 'A';
                for (n = 0; n < pattern_len; n++) {
                    pattern[n] = pindex++;
                    if (pindex > 'Z' && pindex < 'a')
                        pindex = 'a';
                    else if (pindex > 'z')
                        pindex = 'A';
                }
                break;

            case 'b':
                pattern_len = atoi(optarg);
                if (pattern_len > MAX_PATTERN) {
                    fprintf(stderr, "Error: Pattern length too long. "
                            "Current limit is: %u.\n", MAX_PATTERN);
                    exit(EXIT_FAILURE);
                }
                for (n = 0; n < pattern_len; n++)
                    pattern[n] = n;
                break;

            case 'f':
                if ((in_file = fopen(optarg, "r")) == NULL)
                    error_at_line(EXIT_FAILURE, errno, __FILE__, __LINE__,
                            "FATAL ERROR");

                n = 0;
                while (!feof(in_file) && (n < MAX_PATTERN))
                    pattern[n++] = fgetc(in_file);
                pattern_len = --n;
                fclose(in_file);
                break;

            case 'n':
                pattern_len = atoi(optarg);
                if (pattern_len > MAX_PATTERN) {
                    fprintf(stderr, "Error: Pattern length too long. "
                            "Current limit is: %u.\n", MAX_PATTERN);
                    exit(EXIT_FAILURE);
                }
                pindex = '0';
                for (n = 0; n < pattern_len; n++) {
                    pattern[n] = pindex++;
                    if (pindex > '9')
                        pindex = '0';
                }
                break;

            case 'r':
                pattern_len = atoi(optarg);
                if (pattern_len > MAX_PATTERN) {
                    fprintf(stderr, "Error: Pattern length too long. "
                            "Current limit is: %u.\n", MAX_PATTERN);
                    exit(EXIT_FAILURE);
                }
                for (n = 0; n < pattern_len; n++)
                    pattern[n] = (double) random() / RAND_MAX * 256;
                break;

            case 's':
                strncpy(pattern, optarg, MAX_PATTERN - 1);
                pattern[MAX_PATTERN - 1] = '\0';
                pattern_len = strlen(pattern);
                break;

            case 'x':
                hex_stringp = optarg;
                hex_string_len = strlen(hex_stringp);

                if ((hex_string_len % 2) != 0) {
                    fprintf(stderr, "Wrong <hex_string> parameter: "
                            "it must have an even number of digits.\n");
                    exit(EXIT_FAILURE);
                }

                pattern_len = hex_string_len / 2;

                if (pattern_len > MAX_PATTERN) {
                    fprintf(stderr, "Error: Pattern length too long. "
                            "Current limit is: %u.\n", MAX_PATTERN);
                    exit(EXIT_FAILURE);
                }

                for (n = 0; n < hex_string_len; n++)
                    if (!isxdigit(*(hex_stringp + n))) {
                        fprintf(stderr, "Wrong <hex_string> parameter: "
                                "contains a non hexadecimal digit.\n");
                        exit(EXIT_FAILURE);
                    }

                n = 0;
                hex_byte[2] = '\0';
                while (n < hex_string_len) {
                    hex_byte[0] = *(hex_stringp + n++);
                    hex_byte[1] = *(hex_stringp + n++);
                    pattern[n/2 - 1] = strtol(hex_byte, NULL, 16);
                }
                break;

            case 'h':
                printf("Data Pattern Generator\n"
                        "(it generates arbitrary quantity "
                        "of data using a defined pattern)\n"
                        "Usage: %s [ -a <alphabet_len> | -b <binary_len> | "
                        "-f file |\n"
                        "         -n <numeric_len> | -r <random_len> |\n"
                        "         -s <string> | -x <hex_string> ] [-h] "
                        "<data_size>\n", argv[0]);
                printf(" data_size      Amount of data to be generated "
                        "(bytes; S.I. G,M,K units allowed)\n");
                printf(" alphabet_len   Alphabetical pattern length\n");
                printf(" binary_len     Binary pattern length\n");
                printf(" file           File pattern\n");
                printf(" numeric_len    Numeric pattern length\n");
                printf(" random_len     Random pattern length\n");
                printf(" string         Pattern string\n");
                printf(" hex_string     Pattern string "
                        "(hexadecimal without 0x)\n");
                printf("Maximum pattern length (bytes): %d\n", MAX_PATTERN);
                printf("Default pattern: %s\n", DEFAULT_PATTERN);
                exit(EXIT_SUCCESS);
                break;

            default:
                fprintf(stderr, "Invalid option. Use -h for help.\n");
                exit(EXIT_FAILURE);
       }
    }

    if (optind  >= argc) {
        fprintf(stderr, "Missing <data_size> parameter. "
                "Use -h for help.\n");
        exit(EXIT_FAILURE);
    }

    data_size_str = argv[optind]; 

    data_size = strtoul(data_size_str, &unitp, 10);
    if (((data_size == 0) && (unitp == data_size_str))) {
        fprintf(stderr, "Wrong <data_size> parameter. "
                "Use -h for help.\n");
        exit(EXIT_FAILURE);
    }

    switch (*unitp) {
        case 'G':
            data_size = data_size*1000000000;
            break;
        case 'M':
            data_size = data_size*1000000;
            break;
        case 'K':
            data_size = data_size*1000;
            break;
        case '\0':
            break;
        default:
            fprintf(stderr, "Unexpected unit '%c' in <data_size> "
                    "parameter. Use -h for help.\n", *unitp);
            exit(EXIT_FAILURE);
    }

    pindex = 0;
    for (n = 0; n < data_size; n++) {
        putchar(pattern[pindex++]);
        if (pindex == pattern_len) {
            pindex = 0;
        }
    }

    exit(EXIT_SUCCESS);
}
